package org.dru.clay.rhino.feature.zip;

import org.dru.clay.rhino.feature.Feature;
import org.dru.clay.rhino.util.RhinoUtils;
import org.dru.clay.util.file.FileFunctions;
import org.dru.clay.util.file.FileUtils;
import org.dru.clay.util.functional.CollectionUtils;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.Function;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

/**
 * Zip
 * User: joakimd
 * Date: 2013-07-22
 * Time: 14:04
 */
public final class Zip extends Feature {
    private static final String[] FUNCTIONS_NAMES = {
            "Zip_pack",
            "Zip_unpack"
    };

    @Override
    public void init(final Context context, final ScriptableObject scope) throws Exception {
        scope.defineFunctionProperties(FUNCTIONS_NAMES, Zip.class, ScriptableObject.DONTENUM);
        loadFeatureScript(context, scope, Zip.class);
    }

    public static Object Zip_pack(final Context context, final Scriptable thisObj,
                                  final Object[] args, final Function funcObj) throws Exception {
        final File from = new File(Context.toString(args[0]));
        final File into = new File(Context.toString(args[1]));
        final Collection<File> files = CollectionUtils.transform(RhinoUtils.toCollection((Scriptable) args[2]), FileFunctions.file());
        final ZipOutputStream out = new ZipOutputStream(new FileOutputStream(into));
        try {
            for (final File source : files) {
                final String file = FileUtils.relative(from, source);
                final ZipEntry destination = new ZipEntry(file.replace("\\", "/"));
                out.putNextEntry(destination);
                try {
                    FileUtils.copy(source, out);
                    destination.setTime(source.lastModified());
                } finally {
                    out.closeEntry();
                }
            }
            return into.getPath();
        } finally {
            out.close();
        }
    }

    public static Object Zip_unpack(final Context context, final Scriptable thisObj,
                                    final Object[] args, final Function funcObj) throws Exception {
        final File from = new File(Context.toString(args[0]));
        final File into = new File(Context.toString(args[1]));
        into.mkdirs();
        final ZipInputStream in = new ZipInputStream(new FileInputStream(from));
        try {
            final Collection<String> result = new ArrayList<String>();
            ZipEntry entry;
            while ((entry = in.getNextEntry()) != null) {
                final String file = entry.getName();
                final File destination = new File(into, file);
                // even add empty directories if any.
                if (entry.isDirectory()) {
                    destination.mkdirs();
                } else {
                    FileUtils.copy(in, destination);
                    destination.setLastModified(entry.getTime());
                    result.add(destination.getPath());

                }
            }
            return context.newArray(thisObj, result.toArray(new Object[result.size()]));
        } finally {
            in.close();
        }
    }
}
